package com.xiaouyudeguang.common.resource;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xiaouyudeguang.common.annotation.ApiMapping;
import com.xiaouyudeguang.common.annotation.ApiResource;
import com.xiaouyudeguang.common.api.AbstractApi;
import com.xiaouyudeguang.common.constants.Headers;
import com.xiaouyudeguang.common.dao.AbstractDao;
import com.xiaouyudeguang.common.exception.BusinessException;
import com.xiaouyudeguang.common.request.RequestDto;
import com.xiaouyudeguang.common.respose.ResultDto;
import com.xiaouyudeguang.common.service.AbstractService;
import com.xiaouyudeguang.common.utils.EncryptUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class AbstractResource<Service extends AbstractService<? extends AbstractDao<T>, T>, T> implements AbstractApi<T> {

    protected Logger log = LoggerFactory.getLogger(getClass());

    protected Class<T> modelClass = currentModelClass();

    @Autowired
    protected Service baseService;

    @Autowired
    protected HttpServletRequest request;

    @Autowired
    protected HttpServletResponse response;

    @Value("spring.security.signKey:")
    private String signKey;

    public T doFilter(T data) {
        return data;
    }

    public T doFill(T data) {
        return data;
    }

    public RequestDto<T> doFill(RequestDto<T> requestDto) throws Exception {
        requestDto.setData(doFill(requestDto.getData()));
        return requestDto;
    }

    @Autowired
    protected ApplicationContext context;

    public <B> B get(Class<B> cls) {
        return context.getBean(cls);
    }

    @Override
    @ApiMapping(value = "/getById/{id}", method = RequestMethod.GET, tags = "根据id查询")
    public ResultDto<T> getById(String tenantId, String appId, String id) throws Exception {
        return ResultDto.ok(doFilter(baseService.getById(tenantId, appId, id)));
    }

    public ResultDto<T> getOne(String tenantId, String appId, SFunction<T, ?> function, Object value) throws Exception {
        return ResultDto.ok(doFilter(baseService.getOne(tenantId, appId, function, value)));
    }

    @Override
    @ApiMapping(method = RequestMethod.POST, tags = "根据id批量查询", value = "/getByIds")
    public ResultDto<List<T>> getByIds(String tenantId, String appId, Set<String> ids) throws Exception {
        if (ids.isEmpty()) {
            return ResultDto.ok(new ArrayList<>());
        }
        List<T> list = baseService.getByIds(tenantId, appId, ids);
        if (list == null) {
            list = new ArrayList<>();
        }
        return ResultDto.ok(list.stream().map(this::doFilter).collect(Collectors.toList()));
    }

    @Override
    @ApiMapping(value = "/deleteById/{id}", method = RequestMethod.DELETE, tags = "根据id删除")
    public ResultDto<Boolean> deleteById(String tenantId, String appId, String id) throws Exception {
        return ResultDto.ok(baseService.deleteById(tenantId, appId, id));
    }

    @Override
    @ApiMapping(value = "/deleteByIds", method = RequestMethod.DELETE, tags = "根据id批量删除")
    public ResultDto<Boolean> deleteByIds(String tenantId, String appId, Set<String> ids) throws Exception {
        if (ids.isEmpty()) {
            return ResultDto.ok(false);
        }
        return ResultDto.ok(baseService.deleteByIds(tenantId, appId, ids));
    }

    @Override
    @ApiMapping(value = "/updateById", method = RequestMethod.PUT, tags = "根据id更新")
    public ResultDto<Boolean> updateById(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        return ResultDto.ok(baseService.updateById(tenantId, appId, doFill(requestDto)) != null);
    }

    @Override
    @ApiMapping(value = "/updateByIds", method = RequestMethod.PUT, tags = "根据id批量更新")
    public ResultDto<Boolean> updateByIds(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        return ResultDto.ok(baseService.updateByIds(tenantId, appId, doFill(requestDto)) != null);
    }

    @Override
    @ApiMapping(value = "/updateBatch", method = RequestMethod.PUT, tags = "批量更新")
    public ResultDto<Boolean> updateBatch(String tenantId, String appId, List<T> dataList) throws Exception {
        return ResultDto.ok(baseService.updateBatch(tenantId, appId, dataList.stream().map(this::doFilter).collect(Collectors.toList())) != null);
    }

    @Override
    @ApiMapping(value = "/modify", method = RequestMethod.PUT, tags = "修改")
    public ResultDto<Boolean> modify(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        return ResultDto.ok(baseService.modify(tenantId, appId, doFill(requestDto)) != null);
    }

    @Override
    @ApiMapping(value = "/save", method = RequestMethod.POST, tags = "保存")
    public ResultDto<Boolean> save(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        return ResultDto.ok(baseService.save(tenantId, appId, doFill(requestDto)) != null);
    }

    @Override
    @ApiMapping(value = "/saveAndGet", method = RequestMethod.POST, tags = "保存或查询")
    public ResultDto<T> saveAndGet(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        return ResultDto.ok(baseService.save(tenantId, appId, doFill(requestDto)));
    }

    @Override
    @ApiMapping(value = "/saveBatch", method = RequestMethod.POST, tags = "批量保存")
    public ResultDto<Boolean> saveBatch(String tenantId, String appId, List<T> dataList) throws Exception {
        return ResultDto.ok(baseService.saveBatch(tenantId, appId, dataList.stream().map(this::doFilter).collect(Collectors.toList())) != null);
    }

    @Override
    @ApiMapping(value = "/getOne", method = RequestMethod.POST, tags = "单条查询")
    public ResultDto<T> getOne(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        return ResultDto.ok(doFilter(baseService.getOne(tenantId, appId, doFill(requestDto))));
    }

    @Override
    @ApiMapping(value = "/exist", method = RequestMethod.POST, tags = "存在校验")
    public ResultDto<Boolean> exist(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        return ResultDto.result(0, baseService.exist(tenantId, appId, requestDto), null);
    }

    public ResultDto<List<T>> list(String tenantId, String appId, SFunction<T, ?> function, Object value) throws Exception {
        List<T> list = baseService.list(tenantId, appId, function, value);
        if (list == null) {
            list = new ArrayList<>();
        }
        return ResultDto.ok(list.stream().map(this::doFilter).collect(Collectors.toList()));
    }

    public ResultDto<List<T>> list(String tenantId, String appId, SFunction<T, ?> function, Set<?> values) throws Exception {
        List<T> list = baseService.list(tenantId, appId, function, values);
        if (list == null) {
            list = new ArrayList<>();
        }
        return ResultDto.ok(list.stream().map(this::doFilter).collect(Collectors.toList()));
    }

    @Override
    @ApiMapping(value = "/list", method = RequestMethod.POST, tags = "列表查询")
    public ResultDto<List<T>> list(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        List<T> list = baseService.list(tenantId, appId, doFill(requestDto));
        if (list == null) {
            list = new ArrayList<>();
        }
        return ResultDto.ok(list.stream().map(this::doFilter).collect(Collectors.toList()));
    }

    @Override
    public ResultDto<List<T>> tree(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        return ResultDto.ok(baseService.tree(tenantId, appId, doFill(requestDto)));
    }

    @Override
    @ApiMapping(value = "/listAll", method = RequestMethod.POST, tags = "列表查询(后端)")
    public ResultDto<List<T>> listAll(String tenantId, String appId, String from) throws Exception {
        return ResultDto.ok(baseService.listAll(tenantId, appId));
    }

    @Override
    @ApiMapping(value = "/listAllIds", method = RequestMethod.POST, tags = "查询所有id(后端)")
    public ResultDto<Set<String>> listAllIds(String tenantId, String appId, String from) throws Exception {
        return ResultDto.ok(emptySet());
    }

    @Override
    @ApiMapping(value = "/count", method = RequestMethod.POST, tags = "统计总数(后端)")
    public ResultDto<Long> count(@RequestHeader(Headers.TENANT_ID) String tenantId,
                                   @RequestHeader(Headers.APP_ID) String appId,
                                   @RequestHeader(name = Headers.FROM) String from,
                                   @RequestBody RequestDto<T> requestDto) throws Exception {
        return ResultDto.ok(baseService.pageAll(tenantId, appId, requestDto).getTotal());
    }

    @Override
    @ApiMapping(value = "/page", method = RequestMethod.POST, tags = "分页查询(前端)")
    public ResultDto<Object> page(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        return ResultDto.ok(baseService.page(tenantId, appId, doFill(requestDto)));
    }

    @Override
    @ApiMapping(value = "/pageAll", method = RequestMethod.POST, tags = "分页查询(后端)")
    public ResultDto<Page<T>> pageAll(String tenantId, String appId, String from, RequestDto<T> requestDto) throws Exception {
        return ResultDto.ok(baseService.pageAll(tenantId, appId, doFill(requestDto)));
    }

    @Override
    @ApiMapping(value = "/export", method = RequestMethod.POST, tags = "数据导出")
    public void export(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        ApiResource apiResource = this.getClass().getAnnotation(ApiResource.class);
        Class<T> modelClass = this.currentModelClass();
        List<T> recordList = baseService.export(tenantId, appId, doFill(requestDto));
        if (isEmpty(recordList)) {
            throw new BusinessException("没有需要导出的数据");
        }
        String fileName = isNotBlank(requestDto.getKeyword()) ? requestDto.getKeyword() : apiResource.tags()[0].replace("表接口", "_" + System.currentTimeMillis());
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        ExcelWriterSheetBuilder excelWriterBuilder = EasyExcel.write(response.getOutputStream(), modelClass).sheet(fileName);
        if (isNotEmpty(requestDto.getQueryKeys())) {
            excelWriterBuilder.includeColumnFieldNames(requestDto.getQueryKeys());
        }
        excelWriterBuilder.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
        excelWriterBuilder.doWrite(recordList);
    }

    @Override
    @ApiMapping(value = "/upload", method = RequestMethod.POST, tags = "数据导入")
    public ResultDto<Object> upload(String tenantId, String appId, MultipartFile file) throws Exception {
        Class<T> modelClass = this.currentModelClass();
        if (file.getSize() > 26214400) {
            throw new BusinessException("上传文件不能大于25MB");
        }
        List<T> dataList = new ArrayList<>();
        EasyExcel.read(file.getInputStream(), modelClass, new ReadListener<T>() {
            @Override
            public void invoke(T data, AnalysisContext context) {
                dataList.add(data);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {

            }
        }).sheet().doRead();
        baseService.upload(tenantId, appId, dataList.stream().map(this::doFilter).collect(Collectors.toList()));
        return ResultDto.ok();
    }

    @ApiMapping(value = "/encrypt", method = RequestMethod.PUT, tags = "数据加密")
    public ResultDto<Boolean> encrypt(@RequestHeader(Headers.TENANT_ID) String tenantId,
                                      @RequestHeader(Headers.APP_ID) String appId,
                                      @RequestBody Set<String> encodeFields) throws IllegalAccessException {
        List<T> dataList = baseService.list();
        Field[] fields = currentModelClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.getType() == String.class && (encodeFields.contains(field.getName()) || field.getName().toLowerCase().equalsIgnoreCase("phone") || field.getName().equalsIgnoreCase("idcard"))) {
                field.setAccessible(true);
                for (T data : dataList) {
                    if (field.get(data) != null) {
                        field.set(data, EncryptUtils.encrypt((String) field.get(data)));
                    }
                }
            }
        }
        return ResultDto.ok(baseService.updateBatchById(dataList));
    }
}
